﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Db4objects.Db4o;
using Db4objects.Db4o.Config;
using Db4objects.Db4o.TA;
using DonNicky.Db;
using DonNicky.Db.Db4o;
using DonNicky.Db.Db4o.Config.Attributes;
using Db4objects.Db4o.Config.Attributes;

namespace Example
{
    class Program
    {
        readonly static string databaseFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "example.db4o");

        static void Main(string[] args)
        {
            File.Delete(databaseFileName);
            IEmbeddedConfiguration cfg = Db4oEmbedded.NewConfiguration();
            cfg.Common.Add(new TransparentPersistenceSupport(new DeactivatingRollbackStrategy()));
            cfg.UseExtensions(Assembly.GetAssembly(typeof(Company)));
            using (IObjectContainer db = Db4oEmbedded.OpenFile(cfg, databaseFileName))
            {
                db.UseExtensions();
                Company company = new Company { Id = 1, Name = "Apple, Inc." };
                db.Store(company);
                // exception will be thrown on COMMIT, because the same company name is used
                //db.Store(new Company { Id = 2, Name = "Apple, Inc." });
                Company company2 = new Company { Id = 2, Name = "Google, Inc." };
                db.Store(company2);
                db.Commit();
                Department[] depts = new Department[] { new Department { Company = company, Name = "Sales" },
                                                        new Department { Company = company, Name = "Support" },
                                                        new Department { Company = company2, Name = "Sales" }};
                db.Store(depts);
                // exception will be thrown on COMMIT, because department with this name has already been created in Apple, Inc.
                //db.Store(new Department { Company = company, Name = "Sales" });
                db.Commit();

                Employee emp = new Employee { Department = depts[0], Id = 1, FirstName = "John", LastName = "Doe" };
                db.Store(emp);
                // exception  will be thrown on COMMIT into db
                // because employee with this full name already exists independently of company and department
                //db.Store(new Employee { Department = depts[2], Id = 2, FirstName = "John", LastName = "Doe" });
                db.Commit();

                // DEFERRED DELETION DEMONSTRATION
                // mark companies for deletion
                company.Delete();
                company2.Delete();
                // mark for deletion all dependent objects (departments in out case) that have cascade deletion behavior
                db.DeleteCascadeAll();
                // make real deletions
                db.Purge();
                // RESULT:
                // company2 and all its departments will be deleted
                // company will NOT be deleted because it has a department with an employee which cascade deletion is prohibited
                // Support department of company will be deleted because it is empty
            }
        }

        [TransparentPersisted]
        public class Company : Db4oTPEntity
        {
            // single field key
            [Unique]
            private int _id;
            // alternative single field key
            [Unique]
            private string _name;
            private string _fullName;
            private string _address;
            private string _email;
            private string _phone, _fax;

            // only positive values allowed
            [Range(1, int.MaxValue)]
            public int Id { get { return _id; } set { _id = value; } }
            // required property
            [Required]
            public string Name { get { return _name; } set { _name = value; } }
            public string FullName { get { return _fullName; } set { _fullName = value; } }
            public string Address { get { return _address; } set { _address = value; } }
            public string Email { get { return _email; } set { _email = value; } }
            public string Phone { get { return _phone; } set { _phone = value; } }
            public string Fax { get { return _fax; } set { _fax = value; } }

            public override string ToString() { return String.Format("({0}) {1}", Id, Name); }
            public static implicit operator string(Company company) { return company.ToString(); }
        }

        [TransparentPersisted]
        public class Department : Db4oTPEntity
        {
            // composite key (_company, _name)
            // also deletion of a company leads to cascade deletion of all its departments 
            [UniqueGroup(1), DeleteStrategy(DeleteBehavior.Cascade)]
            private Company _company;
            [UniqueGroup(1)]
            private string _name;
            private string _fullName;
            private int _sortMetric;

            // required property
            [Required]
            public Company Company { get { return _company; } set { _company = value; } }
            // required property
            [Required]
            public string Name { get { return _name; } set { _name = value; } }
            public string FullName { get { return _fullName; } set { _fullName = value; } }
            public int SortMetric { get { return _sortMetric; } set { _sortMetric = value; } }

            public override string ToString() { return String.Format("({0}) {1}", Company.Id, Name); }
            public static implicit operator string(Department dept) { return dept.ToString(); }
        }

        [TransparentPersisted]
        public class Employee : Db4oTPEntity
        {
            // composite key (_company, _id)
            [UniqueGroup(1)]
            private Company _company;
            [UniqueGroup(1)]
            private int _id;
            // alternative key (_firstName, _middleName, _lastNamem, _dateOfBirth)
            [UniqueGroup(2)]
            private string _firstName, _middleName, _lastName;
            [UniqueGroup(2)]
            private DateTime _dateOfBirth;
            // prevent deletion of a deparment that has employees
            // DeleteStrategy(DeleteBehavior.Restrict) can be omitted, because it is default behavior
            [Indexed, DeleteStrategy(DeleteBehavior.Restrict)]
            private Department _department;

            public Company Company { get { return _company; } }
            [Range(1, int.MaxValue)]
            public int Id { get { return _id; } set { _id = value; } }
            [Required]
            public string FirstName { get { return _firstName; } set { _firstName = value; } }
            public string MiddleName { get { return _middleName; } set { _middleName = value; } }
            [Required]
            public string LastName { get { return _lastName; } set { _lastName = value; } }
            public string FullName
            {
                get
                {
                    return String.Format("{0} {1} {2}", LastName, FirstName, MiddleName);
                }
            }
            public DateTime DateOfBirth { get { return _dateOfBirth; } set { _dateOfBirth = value; } }
            [Required]
            public Department Department { get { return _department; } set { _department = value; _company = value.Company; } }

            public override string ToString() { return String.Format("({0}/{1}) {2}", Company.Id, Id, FullName); }
            public static implicit operator string(Employee emp) { return emp.ToString(); }
        }

    }
}
